package com.example.jtt1078_audio.reader;

import android.util.Log;

import com.example.jtt1078_audio.encoder.audio.G711Codec;
import com.example.jtt1078_audio.interfaces.OnDataAvailable;

import java.io.InputStream;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadReader extends Thread {

    private static final String TAG = ThreadReader.class.getName();
    private InputStream is;
    private int bufferSize = 1024 * 4;
    private byte[] buffer;
    private byte[] tempStore;
    private G711Codec g711Codec;
    private int startOffset = 26;

    private ExecutorService executorService;

    private OnDataAvailable onDataAvailable;

    public void setOnDataAvailable(OnDataAvailable onDataAvailable) {
        this.onDataAvailable = onDataAvailable;
    }

    public ThreadReader(InputStream is) {
        this.is = is;
        buffer = new byte[bufferSize];
        tempStore = new byte[0];
        g711Codec = new G711Codec();
        executorService = Executors.newSingleThreadExecutor();
    }

    @Override
    public void run() {
        super.run();
        while (!interrupted()){
            try {
                if(is == null){
                    Log.e(TAG, "inputStream is null");
                    return;
                }
                int len;
                while ((len = is.read(buffer)) != -1){
                    byte[] totalData = Arrays.copyOfRange(buffer, 0, len);
//                    byte[] validData = Arrays.copyOfRange(buffer, startOffset, len);
//                    Log.d(TAG, "receive len "+ len + " after len " + validData.length);
//                    Log.d(TAG, bytes2Hex(totalData));
//                    Log.d(TAG, bytes2Hex(validData));

//                    int finalLen = validData.length;
                    if(tempStore.length > 0){
                        totalData = concat(tempStore, totalData);
                        tempStore = new byte[0];
                    }
                    int finalLen = len;
//                    Log.d(TAG, "finalLen "+finalLen);
                    for(int offset = 0; offset < finalLen; ){
                        boolean valid = checkHeader(totalData, offset);
                        if(valid){
                            boolean isAudio = padLeft(Integer.toHexString(totalData[offset + 15]), 2).charAt(0) == '3';
//                            Log.d(TAG, "isAudio "+isAudio);
                            if(isAudio){
                                int dataLen = Integer.parseInt(
                                        padLeft(Integer.toHexString(totalData[offset + 24]), 2) +
                                                padLeft(Integer.toHexString(totalData[offset + 25]), 2),
                                        16
                                );
//                                Log.d(TAG, "dataLen "+ dataLen);
//                                Log.d(TAG, "offset + startOffset + dataLen > len ? " + (offset + startOffset + dataLen > len));
                                if(offset + startOffset + dataLen > len){
                                    tempStore = concat(tempStore, totalData);
                                    break;
                                }else{
                                    int finalOffset = offset;
                                    int diffOffset = 4; // offset 4, I don't know where it comes from
                                    byte[] validData = Arrays.copyOfRange(
                                            totalData,
                                            finalOffset + startOffset + diffOffset,
                                            finalOffset + startOffset + dataLen
                                    );
                                    int validDataLen = validData.length;
//                                    Log.d(TAG, "validData len "+validData.length);
//                                    Log.d(TAG, bytes2Hex(validData));
                                    if(!executorService.isShutdown()){
                                        executorService.execute(()->{
                                            if(onDataAvailable != null){
                                                onDataAvailable.onData(
                                                        g711Codec.decode(
                                                                validData,
                                                                0,
                                                                validDataLen
                                                        )
                                                );
                                            }
                                        });
                                    }

                                    offset += startOffset + dataLen;
//                                    Log.d(TAG, "offset "+offset);
                                }
                            }else{
                                break;
                            }
                        }else{
                            break;
                        }

                    }


                }
            }catch (Exception e){
                e.printStackTrace();
                return;
            }
        }
        executorService.shutdownNow();
    }

    private boolean checkHeader(byte[] data, int offset){
        return data[offset] == 48 && data[offset + 1] == 49
                && data[offset + 2] == 99 && data[offset + 3] == 100;
    }

    private String bytes2Hex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            int i = ((int) b) & 0xFF;
            String hex = Integer.toHexString(i);
            if (hex.length() < 2) {
                hex = "0"+hex;
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    private String padLeft(String str, int len){
        return padLeft(str, len, '0');
    }

    private String padLeft(String str, int len, char c){
        StringBuilder sb = new StringBuilder(str);
        int length = str.length();
        for(int i = length; i < len; i++){
            sb.insert(0, c);
        }
        return sb.toString();
    }

    private byte[] concat(byte[] a, byte[] b){
        byte[] c = new byte[a.length + b.length];
        System.arraycopy(a, 0, c, 0, a.length);
        System.arraycopy(b, 0, c, a.length, b.length);
        return c;
    }
}
